package com.lytoyo.neihanduanzi.controller;

import cn.hutool.core.util.StrUtil;
import com.lytoyo.neihanduanzi.aspect.Login;
import com.lytoyo.neihanduanzi.model.Result;

import com.lytoyo.neihanduanzi.service.CosService;
import com.lytoyo.neihanduanzi.utils.AuthContextHolder;
import com.lytoyo.neihanduanzi.utils.Constant;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.model.PutObjectRequest;
import io.minio.*;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;


/**
 * Package:com.lytoyo.neihanduanzi.controller
 *
 * @ClassName:FileUploadController
 * @Create:2025/1/8 23:22
 **/
@RestController
@RequestMapping("/file")
@Slf4j
public class FileUploadController {
    @Resource
    private MinioClient minioClient;

    @Value("${minio.bucket}")
    private String bucket;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Value("${cos.bucketName}")
    private String bucketName;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private COSClient cosClient;

    /**
     * 上传文件分片
     *
     * @param file         文件
     * @param uuid         文件的md5值
     * @param currentIndex 当前文件分片的下标
     * @param chuckCount   一共有多少文件分片
     * @param fileSuffix   文件名称后缀，即文件类型
     * @return
     */
    @Login
    @PostMapping("/uploadChuck")
    public Result fileUploadChuck(@RequestParam("file") MultipartFile file,
                                  @RequestParam("uuid") String uuid,
                                  @RequestParam("currentIndex") int currentIndex,
                                  @RequestParam("chuckCount") int chuckCount,
                                  @RequestParam("fileSuffix") String fileSuffix,
                                  @RequestParam("title") String title) throws Exception {
        Long userId = AuthContextHolder.getUserId();
        //检查存储桶是否存在,不存在则创建
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }
        //上传文件分片
        //uuid+currentIndex+.+fileSuffix
        String temFileName = uuid + currentIndex + "." + fileSuffix;
        try {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .object(temFileName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .build());
            //判断当前上传是否为最后一次，如果是则进行文件合并
            if (currentIndex + 1 == chuckCount) {
                Thread.sleep(1000);
                try {
                    boolean flag = true;
                    //获取每个在minio中的分片
                    ArrayList<ComposeSource> sources = new ArrayList<>();
                    //判断该文件的所有分片是否都已经上传成功

                    for (int i = 0; i < chuckCount; i++) {
                        String temName = uuid + i + "." + fileSuffix;
                        sources.add(ComposeSource.builder().bucket(bucket).object(temName).build());
                    }

                    //使用uuid+文件类型作为文件名称
                    String fileName = uuid + "." + fileSuffix;
                    //执行合并操作
                    minioClient.composeObject(
                            ComposeObjectArgs.builder()
                                    .bucket(bucket)
                                    .object(fileName)
                                    .sources(sources)
                                    .build());
                    HashMap<String, Object> map = new HashMap<>();
                    map.put("userId", userId);
                    map.put("chuckCount", chuckCount);
                    map.put("uuid", uuid);
                    map.put("fileSuffix", fileSuffix);
                    map.put("title", title);
                    Thread.sleep(1500);

                    rabbitTemplate.convertAndSend(Constant.EXCHANGE_DIRECT_NEIHANDUANZI_VIDEO_PART,
                            Constant.ROUTING_KEY_NEIHANDUANZI_VIDEO_PART,
                            map,
                            message -> {
                                //map 作为消息体，RabbitTemplate 会自动进行序列化
                                //将消息类型设置为json格式
                                message.getMessageProperties().setContentType(MessageProperties.CONTENT_TYPE_JSON);
                                return message;
                            });
                    log.info("合并完成");
                    return Result.success("上传成功");
                } catch (Exception e) {
                    return Result.fail(-1, "上传失败");
                }
            }
            return Result.success("");
        } catch (Exception e) {
            return Result.fail(-1, "上传失败,请重试");
        }
    }

    /**
     * 上传图片
     *
     * @param file
     * @param fileSuffix
     * @return
     * @throws Exception
     */
    @Login
    @PostMapping("/uploadPicture")
    public Result uploadPicture(@RequestParam("file") MultipartFile file,
                                @RequestParam("fileSuffix") String fileSuffix) throws Exception {
        Long userId = AuthContextHolder.getUserId();
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        String fileName = uuid + "." + fileSuffix;
        try {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .object(fileName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .build());
            fileName = "http://175.178.183.104:9000/neihanduanzi/" + fileName;
            return Result.success(fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.fail(-1, "数据错误");
    }

    @RequestMapping("/getVideo")
    public void getVideo(@RequestParam("videoName") String videoName,
                         HttpServletRequest request,
                         HttpServletResponse response) throws Exception {
        ServletOutputStream outputStream = response.getOutputStream();
        response.reset();
        //视频文件实际大小
        long contentRangeSize = 0;
        //视频文b件分段字节数组的开头下标
        long contentRangeStart = 0;
        //视频文件分段字节数组的结尾下标
        long contentRangeEnd = 0;
        StatObjectResponse stat = null;

        try {
            // 1. 先获取文件总大小
            stat = minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucket)
                    .object(videoName)
                    .build());
            contentRangeSize = stat.size();

            // 2. 解析Range头，有俩种格式，bytes=0- 和 bytes=0-1024
            String rangeString = request.getHeader(HttpHeaders.RANGE);
            if (StrUtil.isNotBlank(rangeString)) {
                String[] rangeArr = rangeString.split("=");
                //bytes=0-1024情况
                if (rangeArr.length > 1) {
                    String[] rangeData = rangeArr[1].split("-");
                    contentRangeStart = Long.parseLong(rangeData[0]);
                    // 关键：当没有结束位置时，自动设置为文件末尾
                    contentRangeEnd = (rangeData.length > 1 && !rangeData[1].isEmpty()) ?
                            Long.parseLong(rangeData[1]) :
                            contentRangeSize - 1;
                }
                // 确保结束位置不超过文件范围
                contentRangeEnd = Math.min(contentRangeEnd, contentRangeSize - 1);
            }

            // 3. 统一处理所有Range请求（包括无结束位置的情况）
            long chunkLength = contentRangeEnd - contentRangeStart + 1;

            // 设置响应头
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            response.setHeader(HttpHeaders.CONTENT_TYPE, "video/mp4");
            response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
            response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(chunkLength));
            response.setHeader(HttpHeaders.CONTENT_RANGE,
                    String.format("bytes %d-%d/%d", contentRangeStart, contentRangeEnd, contentRangeSize));

            // 4. 流式传输分片数据
            try (GetObjectResponse objectResponse = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucket)
                            .object(videoName)
                            .offset(contentRangeStart)
                            .length(chunkLength)
                            .build())) {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = objectResponse.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            } finally {
                try {
                    if (null != outputStream) {
                        outputStream.close();
                    }
                } catch (Exception e) {
                    log.error("流操作已失效");
                }
            }

        } catch (Exception e) {
            log.error("处理请求失败: {}", e.getMessage());
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        } finally {
            outputStream.flush();
            try {
                if (null != outputStream) {
                    outputStream.close();
                }
            } catch (Exception e) {
                log.error("流操作已失效");
            }
        }
    }

    @Login
    @PostMapping("/chat/upload")
    public Result chatFileUpload(@RequestParam("file") MultipartFile file, @RequestParam("fileType") String fileType, @RequestParam("msgType") Integer msgType) throws Exception {
        Long userId = AuthContextHolder.getUserId();
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        String fileName = uuid + "." + fileType;
        try {

            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .object(fileName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .build());
            if (msgType == 2) {
                fileName = "http://175.178.183.104:9000/neihanduanzi/" + fileName;
            }
            return Result.success(fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.fail(-1, "数据错误");
    }

    /**
     * 上传图片博客
     *
     * @param file
     * @return
     * @throws Exception
     */
    @Login
    @PostMapping("/picture/blog")
    public Result uploadPicutreBlog(@RequestParam("file") MultipartFile file) throws Exception {
        Long userId = AuthContextHolder.getUserId();
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }
        try {
            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            String fileType = file.getContentType().split("/")[1];
            String fileName = uuid + "." + fileType;
            //获取图片类型
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .object(fileName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .build());
            fileName = "http://175.178.183.104:9000/neihanduanzi/" + fileName;
            return Result.success(fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.fail(-1, "数据错误");
    }
}
